﻿using System;
using System.Collections.Generic;
using OverStore.Configuration.Validation;
using OverStore.Exceptions.Configuration;
using OverStore.Runtime.OverStoreContext;
using OverStore.Runtime.ObjectRepository.Depot;
using OverStore.Runtime.ObjectRepository.Depot.IdentifierGenerator;
using OverStore.Configuration.ObjectRepository.Accessors;

namespace OverStore.Configuration.ObjectRepository.Depot
{
    /// <summary>
    /// Contains configuration parameters allows to create new instances of <see cref="IDepot{TInstance, TIdentifier}"/>
    /// uses specified delegate for perform its tasks.
    /// </summary>
    /// <typeparam name="TInstance">The type of the instance.</typeparam>
    /// <typeparam name="TIdentifier">The type of the instance identifier.</typeparam>
    public sealed class DelegateDepotConfiguration<TInstance, TIdentifier> : IComponentConfiguration<IDepot<TInstance, TIdentifier>>,
        ISupportValidation
        where TInstance : class
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="DelegateDepotConfiguration{TInstance, TIdentifier}"/> class.
        /// </summary>
        public DelegateDepotConfiguration()
        {
            StoreIdentifierCopy = EmptyApplyIdentifierImpl;
            CreateInstanceFunction = DefaultCreateInstance;
        }

        /// <summary>
        /// Gets or sets the identifier generator.
        /// </summary>
        public IComponentConfiguration<IIdentifierGenerator<TInstance, TIdentifier>> IdentifierGenerator { get; set; }

        /// <summary>
        /// Gets or sets delegate that updates instance identifier.
        /// </summary>
        public Action<TInstance, TIdentifier> StoreIdentifierCopy { get; set; }

        /// <summary>
        /// Gets or sets the get stored identifier action.
        /// </summary>
        /// <value></value>
        public Func<TInstance, TIdentifier> GetStoredIdentifier { get; set; }

        /// <summary>
        /// Gets or sets delegate that creates new instance of <typeparamref name="TInstance" />.
        /// </summary>
        public Func<TInstance> CreateInstanceFunction { get; set; }

        private static void DefaultApplyStoredIdentifier(TInstance instance, TIdentifier identifier)
        {
        }

        private static TIdentifier DefaultGetStoredIdentifier(TInstance instance)
        {
            return default(TIdentifier);
        }

        /// <summary>
        /// Creates the new instance of <see cref="IDepot{TInstance, TIdentifier}"/> type.
        /// </summary>
        /// <param name="context">The current context provides access to set of services may be used by depot.</param>
        /// <returns>
        /// The new instance of <see cref="IDepot{TInstance, TIdentifier}"/>.
        /// </returns>
        public IDepot<TInstance, TIdentifier> CreateComponent(IOverStoreContext context)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (CreateInstanceFunction == null)
                throw this.ConfigurationException("CreateInstance function is missing.");
            if (IdentifierGenerator == null)
                throw this.ConfigurationException("Identifier Generator is missing.");

            ExpressionAccessor<TInstance, TIdentifier> storedIdentifierAccessor = null;
            if (GetStoredIdentifier != null ||
                StoreIdentifierCopy != null)
                storedIdentifierAccessor = new ExpressionAccessor<TInstance, TIdentifier>(
                    GetStoredIdentifier ?? DefaultGetStoredIdentifier,
                    StoreIdentifierCopy ?? DefaultApplyStoredIdentifier);


            return new GenericDepot<TInstance, TIdentifier>(
                            CreateInstanceFunction, 
                            EqualityComparer<TIdentifier>.Default, 
                            storedIdentifierAccessor,
                            IdentifierGenerator.CreateComponent(context));
        }

        /// <summary>
        /// Perform component validation. Method should validate only
        /// component configuration itself, not sub components.
        /// If configuration is valid, null result should returns.
        /// </summary>
        /// <returns>
        ///     <see cref="T:OverStore.Configuration.ValidationResults"/> describes errors found in component configuration.
        /// </returns>
        public ValidationResult Validate()
        {
            return new ValidationResult(this)
                .AddError(StoreIdentifierCopy == null, "Apply identifier delegate is missing.")
                .AddError(CreateInstanceFunction == null, "Create instance delegate is missing.")
                .AddError(IdentifierGenerator == null, "Identifier Generator is missing.")
                .ValidateToNestedResult(IdentifierGenerator);
        }

        private static void EmptyApplyIdentifierImpl(TInstance instance, TIdentifier identifier)
        {
        }

        private static TInstance DefaultCreateInstance()
        {
            return Activator.CreateInstance<TInstance>();
        }
    }
}